package com.example.crypt;

import com.example.crypt.CryptToolFactory.CryptType;

import java.io.IOException;
import java.io.InputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * 实现{@link ICrypt}，对外提供各种加密方法
 *
 * @author wzq
 * @2014年5月5日
 */
class CryptTool implements ICrypt {
    private static IByteCrypt cryptCore;
    private static final String CHARSET_UTF8 = "UTF-8";

    // singleton
    private static CryptTool sInstance = new CryptTool();

    private CryptTool() {
    }

    public static CryptTool getInstance(CryptType type) {
        switch(type) {
            case AES:
                cryptCore = new TypeAes();
                break;
            case DES:
                cryptCore = new TypeDes();
                break;
            case RSA:
                cryptCore = new TypeRsa();
                break;
            default:
                cryptCore = new TypeDes();
                break;
        }
        cryptCore = new TypeAes();
        return sInstance;
    }

    @Override
    public byte[] encryptBytes(byte[] bytes) {
        return cryptCore.encryptBytes(bytes);
    }

    @Override
    public byte[] decryptBytes(byte[] bytes) {
        return cryptCore.decryptBytes(bytes);
    }

    @Override
    public String encryptString(String str) {
        try {
            // 明文String => byte[] ==加密==> byte[] =>String
            byte[] srcBytes = str.getBytes(CHARSET_UTF8);
            byte[] dstBytes = cryptCore.encryptBytes(srcBytes);
            return bytes2String(dstBytes);
        } catch(IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public String decryptString(String str) {
        try {
            // String => byte[] ==解密==> byte[] =>明文String
            byte[] encryptBytes = string2Bytes(str);
            byte[] decryptBytes = cryptCore.decryptBytes(encryptBytes);
            if(decryptBytes != null) {
                return new String(decryptBytes, CHARSET_UTF8);
            }
        } catch(IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public InputStream encryptStream(InputStream in) {
        try {
            // InputStream => ByteArray => encrypt bytes => InputStream
            byte[] bytes = CryptUtils.stream2Bytes(in);
            byte[] encryptBytes = cryptCore.encryptBytes(bytes);
            return CryptUtils.bytes2Stream(encryptBytes);
        } catch(IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public InputStream decryptStream(InputStream in) {
        try {
            // InputStream => ByteArray => decrypt bytes => InputStream
            byte[] bytes = CryptUtils.stream2Bytes(in);
            byte[] decryptBytes = cryptCore.decryptBytes(bytes);
            return CryptUtils.bytes2Stream(decryptBytes);
        } catch(IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 寻找sql中''部分,进行加密
     */
    @Override
    public String encryptSql(String sql) {
        if(!sql.contains("'")) {
            return sql;
        }
        // 以 '开始结束,中间不包含'
        String strPattern = "'[^']{0,}'";
        Pattern p = Pattern.compile(strPattern);
        Matcher m = p.matcher(sql);
        // 替换后的sql
        StringBuilder sb = new StringBuilder();
        //
        int preEnd = 0;
        while(m.find()) {
            int start = m.start();
            int end = m.end();
            // 加入
            sb.append(sql.subSequence(preEnd, start));
            // 替换
            String foundStr = m.group();
            String tobeEncrypt = foundStr.substring(1, foundStr.length() - 1);
            String encryptStr = encryptString(tobeEncrypt);
            // 加入
            sb.append("'").append(encryptStr).append("'");
            preEnd = end;
        }
        sb.append(sql.substring(preEnd, sql.length()));
        return sb.toString();
    }

    /**
     * 自定义的 byte[] => string,仅用于加密后的byte[] 保证String中只含有 0~F
     */
    private static String bytes2String(byte[] cryptBytes) {
        int len = cryptBytes.length;
        // byte 0xab => String型的:ab , 每个byte用2个字符表示
        StringBuffer sb = new StringBuffer(len * 2);
        for(int i = 0; i < len; i++) {
            sb.append(String.format("%02x", cryptBytes[i]));
        }
        return sb.toString();
    }

    /**
     * 自定义的String => byte[],仅用于 {@link#bytes2String(byte[])}生成的String
     */
    private static byte[] string2Bytes(String str) {
        byte[] arrB = str.getBytes();
        int iLen = arrB.length;
        // 2个字符表示一个byte, 所以字节数组长度是字符串长度除以2
        byte[] arrOut = new byte[iLen / 2];
        for(int i = 0; i < iLen; i = i + 2) {
            String strTmp = new String(arrB, i, 2);
            arrOut[i / 2] = (byte) Integer.parseInt(strTmp, 16);
        }
        return arrOut;
    }
}
